package me.ywy.dbtest.dongnaodb.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import me.ywy.dbtest.dongnaodb.annotation.DBField;
import me.ywy.dbtest.dongnaodb.annotation.DBTable;

/**
 * Created by ywy on 2018/4/20.
 */
public class BaseDao<T> implements IBaseDao<T> {
    //数据库连接
    private SQLiteDatabase sqLiteDatabase;
    //数据库名称
    private String tableName;
    //操作对象Class文件
    private Class<T> mEntity;

    private HashMap<String, Field> cacheMap;
    //是否初始化
    private boolean isInit;

    public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entity) {
        mEntity = entity;
        this.sqLiteDatabase = sqLiteDatabase;


        if (!isInit) {

            //获取数据库表名称
            if (entity.getAnnotation(DBTable.class) != null) {
                //类上使用了注解
                this.tableName = entity.getAnnotation(DBTable.class).value();
            } else {
                //类上没有使用注解
                this.tableName = entity.getSimpleName();
            }

            if (!sqLiteDatabase.isOpen()) {
                throw new RuntimeException("database is not open!");
            }

            //创建数据库
            String createTableSqlStr = getCreateTableSqlString();
            Log.d("dongnao_db", "sql:" + createTableSqlStr);
            sqLiteDatabase.execSQL(createTableSqlStr);
            cacheMap = new HashMap<>();
            initCacheMap();
            isInit = true;
        }

        return isInit;
    }

    private void initCacheMap() {
        String querySql = "select * from " + tableName + " limit 1,0 ";
        Cursor cursor = sqLiteDatabase.rawQuery(querySql, null);
        //获取数据库表多有字段名
        String[] columnNames = cursor.getColumnNames();
        //获取数据库表对应类的所有变量名
        Field[] fields = mEntity.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
        }
        for (String columName : columnNames) {
            Field refField = null;
            for (Field field : fields) {
                String fieldName = null;
                if (field.getAnnotation(DBField.class) != null) {
                    fieldName = field.getAnnotation(DBField.class).value();
                } else {
                    fieldName = field.getName();
                }

                if (TextUtils.equals(fieldName, columName)) {
                    refField = field;
                    break;
                }
            }
            if (refField != null) {
                cacheMap.put(columName, refField);
            }
        }
    }

    private String getCreateTableSqlString() {

        //create table if not exists user (_id INTEGER,name TEXT,password TEXT)
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer.append("create table if not exists " + tableName + " ( ");
        Field[] fields = mEntity.getDeclaredFields();
        for (Field field : fields) {
            Class<?> type = field.getType();
            if (field.getAnnotation(DBField.class) != null) {
                //字段有注解
                if (type == String.class) {
                    sqlBuffer.append(field.getAnnotation(DBField.class).value() + " TEXT ,");
                } else if (type == Integer.class) {
                    sqlBuffer.append(field.getAnnotation(DBField.class).value() + " INTEGER ,");
                } else if (type == Long.class) {
                    sqlBuffer.append(field.getAnnotation(DBField.class).value() + " BIGINT ,");
                } else if (type == Double.class) {
                    sqlBuffer.append(field.getAnnotation(DBField.class).value() + " DOUBLE ,");
                } else if (type == byte[].class) {
                    sqlBuffer.append(field.getAnnotation(DBField.class).value() + " BLOB ,");
                } else {
                    continue;
                }
            } else {
                //字段无注解
                if (type == String.class) {
                    sqlBuffer.append(field.getName() + " TEXT ,");
                } else if (type == Integer.class) {
                    sqlBuffer.append(field.getName() + " INTEGER ,");
                } else if (type == Long.class) {
                    sqlBuffer.append(field.getName() + " BIGINT ,");
                } else if (type == Double.class) {
                    sqlBuffer.append(field.getName() + " DOUBLE ,");
                } else if (type == byte[].class) {
                    sqlBuffer.append(field.getName() + " BLOB ,");
                } else {
                    continue;
                }
            }

        }

        if (sqlBuffer.charAt(sqlBuffer.length() - 1) == ',') {
            sqlBuffer.deleteCharAt(sqlBuffer.length() - 1);
        }

        sqlBuffer.append(")");

        return sqlBuffer.toString();
    }

    @Override
    public long insert(T entity) {
        HashMap<String, String> map = getMapValues(entity);
        ContentValues contentValues = getContentValueByMap(map);
        long result = sqLiteDatabase.insert(tableName, null, contentValues);
        return result;
    }

    @Override
    public long update(T entity, T where) {
        int result = -1;
        Map<String, String> updateMap = getMapValues(entity);
        ContentValues updateValue = getContentValueByMap(updateMap);

        Map<String, String> query = getMapValues(where);
        Condition condition = new Condition(query);
        result = sqLiteDatabase.update(tableName, updateValue, condition.whereClause, condition.whereArgs);
        return result;
    }

    private class Condition {
        private String whereClause;
        private String[] whereArgs;

        public Condition(Map<String, String> queryMap) {
            List<String> list = new ArrayList();
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("1=1");
            Set<String> keySet = queryMap.keySet();
            for (String key : keySet) {
                String value = queryMap.get(key);
                if (value != null) {
                    stringBuffer.append(" and " + key + "=?");
                    list.add(value);
                }
            }

            this.whereClause = stringBuffer.toString();
            Log.d("dongnao_db", "condition_queryclause:" + whereClause);
            this.whereArgs = list.toArray(new String[list.size()]);
            Log.d("dongnao_db", "condition_whereargs:" + whereArgs);
        }

    }

    @Override
    public int delete(T entity) {
        HashMap<String, String> mapValues = getMapValues(entity);
        Condition condition = new Condition(mapValues);
        int result = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs);
        return result;
    }

    private ContentValues getContentValueByMap(Map<String, String> map) {
        ContentValues contentValues = new ContentValues();
        Set<String> keys = map.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = map.get(key);
            if (value != null) {
                contentValues.put(key, map.get(key));
            }
        }
        return contentValues;
    }

    private HashMap<String, String> getMapValues(T entity) {
        HashMap<String, String> map = new HashMap<>();
        Iterator<Field> fieldIterator = cacheMap.values().iterator();
        while (fieldIterator.hasNext()) {
            Field field = fieldIterator.next();
            field.setAccessible(true);

            try {
                Object object = field.get(entity);
                if (object == null) {
                    continue;
                }
                String value = object.toString();
                //获取列名
                String key = null;
                if (field.getAnnotation(DBField.class) != null) {
                    key = field.getAnnotation(DBField.class).value();
                } else {
                    key = field.getName();
                }

                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                    map.put(key, value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    @Override
    public List<T> query(String sql) {
        return null;
    }

    @Override
    public List<T> query(T where) {
        return query(where, null, null, null);
    }

    @Override
    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
        Map<String, String> queryMap = getMapValues(where);
        Condition condition = new Condition(queryMap);
        String limitStr = null;
        if (startIndex != null && limit != null) {
            limitStr = startIndex + " , " + limit;
        }
        Cursor queryCursor = sqLiteDatabase.query(tableName, null, condition.whereClause,
                condition.whereArgs, null, null, orderBy, limitStr);

        List<T> result = getResult(queryCursor, where);

        return result;
    }

    private List<T> getResult(Cursor queryCursor, T where) {
        List resultList = new ArrayList();
        try {
            Object item = null;
            while (queryCursor.moveToNext()) {
                item = where.getClass().newInstance();
                Set<String> keySet = cacheMap.keySet();
                for (String key : keySet) {
                    Field field = cacheMap.get(key);
                    Class<?> type = field.getType();
                    int cursorIndex = queryCursor.getColumnIndex(key);
                    if (cursorIndex != -1) {
                        if (type == String.class) {
                            field.set(item,queryCursor.getString(cursorIndex));
                        } else if (type == Integer.class) {
                            field.set(item,queryCursor.getInt(cursorIndex));
                        } else if (type == Double.class) {
                            field.set(item,queryCursor.getDouble(cursorIndex));
                        } else if (type == Long.class) {
                            field.set(item,queryCursor.getLong(cursorIndex));
                        } else if (type == byte[].class) {
                            field.set(item,queryCursor.getBlob(cursorIndex));
                        } else {
                            continue;
                        }
                    }
                }
                resultList.add(item);

            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        queryCursor.close();
        return resultList;
    }
}
